﻿#define NOMINMAX
#include<cstdio>
#include<fstream>
#include<iostream>
#include<ctime>
#include<stdlib.h>
#include<windows.h>
#include<conio.h>
#include<string>
#include<vector>
#include<sstream>
#include<cassert>
using namespace std;
#define frame_width 90
#define frame_height 30
//食物结构体
typedef struct {
    int x, y;
} Food;
//蛇结构体
typedef struct {
    int x[100], y[100], len, state;
} Snake;
//控制光标的位置
void gotoxy(int x, int y); 
//边界
void print_map();
void border();
//生成新食物
void get_newfood();
//检查新食物有没有在蛇身上
bool check_foodinsnake();
//读取终端信息
void move_snake();
//计算得分
void check_foodeating();
//检测蛇死亡
bool check_snakealive();
//食物颜色
void Food_color(const char* s, int color);
//障碍墙
void Barrier_wall();
//全局变量
int score;
Snake snake;
Food food;
bool check_eaten;
int i = 0;
int main(){
    gotoxy(frame_height / 2 - 3, frame_width / 2 - 10);
    cout << "输入密码" << endl;
    gotoxy(frame_height / 2 - 2, frame_width / 2 - 10);
    system("color F");
    do {
选择:
        //清空输入缓冲区
        cin.ignore(numeric_limits < streamsize > ::max(), '\n');
        system("cls");
        border();
        gotoxy(frame_height / 2 - 3, frame_width / 2 - 10);
        cout << "1:创建你的蛇" << endl;
        gotoxy(frame_height / 2 - 2, frame_width / 2 - 10);
        cout << "2:查看历史记录" << endl;
        char a;
        a = 0;
        gotoxy(frame_height / 2 - 1, frame_width / 2 - 10);
        cin >> a;
        if (a == '2') {
            system("cls");
            border();
            gotoxy(2, frame_width / 2 - 10);
            cout << "历史记录" << endl;
            //读取历史蛇
            ifstream buff("E:/VS库/贪吃蛇/历史蛇记录.txt");
            if (buff) {
                string line;
                int i = 0;
                while (getline(buff, line)) {
                    gotoxy(i + 3, frame_width / 2 - 10);
                    //循环打印
                    cout << line << endl;
                    i++;
                }
                //关闭文件
                buff.close();
                char exit;
                exit = 0;
                gotoxy(i + 3, frame_width / 2 - 10);
                cout << "1:返回" << endl;
                gotoxy(i + 4, frame_width / 2 - 10);
                cin >> exit;
                if (exit == '1')
                    goto 选择;
            }
            else {
                cout << "历史记录调取失败" << endl;
                goto 选择;
            }
        }
        else if (a == '1') {
            char name[2048];
            system("cls");
            border();
            gotoxy(frame_height / 2 - 3, frame_width / 2 - 10);
            cout << "1:输入你的蛇名" << endl;
            //将蛇名发送给历史蛇记录.txt
            ofstream f;
            f.open("E:/VS库/贪吃蛇/历史蛇记录.txt", ios::app);
            gotoxy(frame_height / 2 - 2, frame_width / 2 - 10);
            cin >> name;
            //进行蛇名写入
            f << name;
            //关闭文件
            f.close();
            system("cls");
            border();
            gotoxy(frame_height / 2 - 1, frame_width / 2 - 10);
            cout << "1:开始\t2:退出" << endl;
            gotoxy(frame_height / 2, frame_width / 2 - 10);
            char s = 0;
            cin >> s;
            if (s == '2') {
                system("cls");
                cout << "结束" << endl;
                //将结束蛇名发送给历史蛇记录.txt
                ofstream f;
                f.open("E:/VS库/贪吃蛇/历史蛇记录.txt", ios::app);
                //进行蛇名写入
                f << endl;
                //关闭文件
                f.close();
                return 0;
            }
            else if (s == '1') {
                system("cls");
                border();
                gotoxy(frame_height / 2 - 3, frame_width / 2 - 10);
                cout << "开始" << endl;
                Sleep(1000);
                system("cls");
                print_map();
                score = 0, check_eaten = 0;
                //贪吃蛇的每回合运行控制
                while (1) {
                    check_foodeating();
                    move_snake();
                    //控制速度（与长度呈反比）
                    Sleep(max(50, 300 - score));
                    if (!check_snakealive())
                        break;
                }
                gotoxy(frame_height / 2 - 3, frame_width / 2 - 10);
                printf("嘎了!\t得分:%d\n", score);
                if (score < 520) {
                    gotoxy(frame_height / 2 - 2, frame_width / 2 - 10);
                    cout << "菜就多练" << "输不起就别玩" << endl;
                }
                else if (score > 520 && score < 1500) {
                    gotoxy(frame_height / 2 - 2, frame_width / 2 - 10);
                    cout << "你真棒呀" << endl;
                }
                else {
                    gotoxy(frame_height / 2 - 2, frame_width / 2 - 10);
                    cout << "好不好玩，还不听课？" << endl;
                }
                //将蛇名分数发送给历史蛇记录.txt
                ofstream f;
                f.open("E:/VS库/贪吃蛇/历史蛇记录.txt", ios::app);
                //进行蛇名分数写入
                f << " 得分" << score << endl;
                //关闭文件
                f.close();
                //清空输入缓冲区
                cin.ignore(numeric_limits < streamsize > ::max(), '\n');
                gotoxy(frame_height / 2 - 1, frame_width / 2 - 10);
                printf("1:开始\t2:结束\n");
                gotoxy(frame_height / 2, frame_width / 2 - 10);
                char com2;
                cin >> com2;
                if (com2 == '2')
                    break;
            }
        }
    } while (1);
}
void Barrier_wall() {

}
void border(){
    //打印墙壁
    for (int i = 0; i < frame_height; i++) {
        gotoxy(i, 0);
        printf("#");
        //因为这个标记是长度，从零开始所以最后要减1
        gotoxy(i, frame_width - 1);
        printf("#");
    }
    for (int i = 0; i < frame_width; i++) {
        gotoxy(0, i);
        printf("#");
        gotoxy(frame_height - 1, i);
        printf("#");
    }
}
void gotoxy(int x, int y){
    //COORD是一种自带结构体，表示一个字符在控制台屏幕上的坐标
    COORD pos;
    //从标准输出设备里取出一个句柄
    HANDLE han = GetStdHandle(STD_OUTPUT_HANDLE);  
    pos.X = y, pos.Y = x;
    //定位光标的函数
    SetConsoleCursorPosition(han, pos);
}
void print_map(){
    //打印墙壁
    for (int i = 0; i < frame_height; i++){
        gotoxy(i, 0);
        printf("#");
        //因为这个标记是长度，从零开始所以最后要减1
        gotoxy(i, frame_width - 1);
        printf("#");
    }
    for (int i = 0; i < frame_width; i++){
        gotoxy(0, i);
        printf("#");
        gotoxy(frame_height - 1, i);
        printf("#");
    }
    //蛇身初始化
    snake.len = 3;
    snake.state = 'w';
    snake.x[1] = frame_height / 2;
    snake.y[1] = frame_width / 2;
    gotoxy(snake.x[1], snake.y[1]);
    printf("@");
    for (int i = 2; i <= snake.len; i++){
        snake.x[i] = snake.x[i - 1] + 1;
        snake.y[i] = snake.y[i - 1];
        gotoxy(snake.x[i], snake.y[i]);
        printf("@");
    }
    //打印初始食物
    get_newfood();
    //打印右边状态栏
    gotoxy(2, frame_width + 3);
    printf("贪吃蛇");
    gotoxy(4, frame_width + 3);
    printf("上:   w");
    gotoxy(6, frame_width + 3);
    printf("下:   s");
    gotoxy(8, frame_width + 3);
    printf("左:   a");
    gotoxy(10, frame_width + 3);
    printf("右:   d");
    gotoxy(12, frame_width + 3);
    printf("得分:%d", score);
    gotoxy(28, frame_width + 3);
    printf("荷蜓心语");
}
bool check_foodinsnake(){
    for (int i = 1; i <= snake.len; i++)
        if (snake.x[i] == food.x && snake.y[i] == food.y)
            return 1;
    return 0;
}
void get_newfood(){
    do {
        srand(time(0));
        food.x = rand() % (frame_height - 2) + 1;
        food.y = rand() % (frame_width - 2) + 1;
    } while (check_foodinsnake());
    gotoxy(food.x, food.y);
    if (i < 16) {
        Food_color("*", i);
        i++;
    }
    else {
        i = 1;
        Food_color("*", i);
        i++;
    }
}
void move_snake(){
    char com = 0;
    //键盘有输入
    while (_kbhit())
        //从控制台读取一个字符，但不显示在屏幕上
        com = _getch();
    //没有吃到去除蛇尾
    if (!check_eaten){
        gotoxy(snake.x[snake.len], snake.y[snake.len]);
        printf(" ");
    }
    //将除蛇头外的其他部分向前移动
    for (int i = snake.len; i > 1; i--)
        snake.x[i] = snake.x[i - 1],
        snake.y[i] = snake.y[i - 1];
    //移动蛇头
    switch (com) {
    case 'w': {
        //如果命令与当前方向相反不起作用
        if (snake.state == 's')
            snake.x[1]++;
        else
            snake.x[1]--, snake.state = 'w';
        break;
    }
    case 's': {
        if (snake.state == 'w')
            snake.x[1]--;
        else
            snake.x[1]++, snake.state = 's';
        break;
    }
    case 'a': {
        if (snake.state == 'd')
            snake.y[1]++;
        else
            snake.y[1]--, snake.state = 'a';
        break;
    }
    case 'd': {
        if (snake.state == 'a')
            snake.y[1]--;
        else
            snake.y[1]++, snake.state = 'd';
        break;
    }
    //按其余键保持状态前进
    default: {
        if (snake.state == 's')
            snake.x[1]++;
        else if (snake.state == 'w')
            snake.x[1]--;
        else if (snake.state == 'd')
            snake.y[1]++;
        else if (snake.state == 'a')
            snake.y[1]--;
        break;
    }
    }
    gotoxy(snake.x[1], snake.y[1]);
    printf("@");
    check_eaten = 0;
    gotoxy(frame_height, 0);
}
void check_foodeating(){
    if (snake.x[1] == food.x && snake.y[1] == food.y){
        score += 10;
        check_eaten = 1;
        gotoxy(12, frame_width + 3);
        printf("得分:%d", score);
        snake.len++;
        get_newfood();
    }
}
bool check_snakealive(){
    //检查有没有撞到墙
    //撞墙
    if (snake.x[1] == 0 || snake.x[1] == frame_height - 1 || snake.y[1] == 0 || snake.y[1] == frame_width - 1)
        return 0;
    //检查有没有吃到自己
    for (int i = 2; i <= snake.len; i++)
        if (snake.x[i] == snake.x[1] && snake.y[i] == snake.y[1])
            return 0;
    return 1;
}
void Food_color(const char* s, int color) {
        HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | color);
        printf(s);
        SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | 7);
}